﻿using Moki.Exceptions;
using Moki;
using Moki.Tests.Model;
using NUnit.Framework;

namespace Moki.Specification
{
    [TestFixture]
    public class VerifySpec
    {
        [TearDown]
        public void TearDown()
        {
            Mock.CleanUp();
        }

        [Test]
        public void Verify_not_stubbed_was_called()
        {
            ITests test = Mock.Create<ITests>();

            test.DoSomething();

            Mock.VerifyCalledWithAnyArgs(() => test.DoSomething());


            Assert.Throws<VerifyException>(() => Mock.VerifyNotCalled(() => test.DoSomething()));
        }

        [Test]
        [ExpectedException(typeof(VerifyException))]
        public void Verify_was_called_throws_exception_when_no_call()
        {
            ITests test = Mock.Create<ITests>();

            Mock.VerifyCalledWithAnyArgs(() => test.DoSomething());
        }

        [Test]
        public void Verify_was_not_called()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.GetSomething()).Returns("That works!");

            Assert.AreEqual("That works!", test.GetSomething());

            Mock.VerifyCalledWithAnyArgs(() => test.GetSomething());
            Mock.VerifyNotCalled(() => test.GetAnswer());
            Assert.Throws<VerifyException>(() => Mock.VerifyCalledWithAnyArgs(() => test.GetAnswer()));
        }

        [Test]
        public void Verify_was_called()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.GetSomething()).Returns("That works!");

            Assert.AreEqual("That works!", test.GetSomething());

            Mock.VerifyCalledWithAnyArgs(() => test.GetSomething());
        }

        [Test]
        public void Verify_was_called_with_arguments()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.ConvertToLang(5)).Returns("five");
            Mock.WhenCalled(() => test.ConvertToLang(-2)).Returns("minus two");

            Assert.AreEqual("five", test.ConvertToLang(5));
            Assert.AreEqual("minus two", test.ConvertToLang(-2));

            Mock.VerifyCalled(() => test.ConvertToLang(5));
            Mock.VerifyCalled(() => test.ConvertToLang(-2));
        }

        [Test]
        [ExpectedException(typeof(VerifyException))]
        public void Verify_was_called_with_arguments_throws_exception_when_no_call()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.ConvertToLang(5)).Returns("five");
            Mock.WhenCalled(() => test.ConvertToLang(-2)).Returns("minus two");

            Mock.VerifyCalled(() => test.ConvertToLang(1));
        }

        [Test]
        public void Verify_was_called_for_arguments_with_any_arg()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.ConvertToLang(Arg.Any<int>())).Returns("any int");

            test.ConvertToLang(1);

            Mock.VerifyCalled(() => test.ConvertToLang(Arg.Any<int>()));
        }

        [Test]
        public void Verify_was_called_with_arguments_when_stubbed_any_arg()
        {
            ITests test = Mock.Create<ITests>();

            Mock.WhenCalled(() => test.ConvertToLang(Arg.Any<int>())).Returns("any int");

            test.ConvertToLang(1);
            test.ConvertToLang(2);

            Mock.VerifyCalled(() => test.ConvertToLang(1));
            Mock.VerifyCalled(() => test.ConvertToLang(2));
        }

        [Test]
        public void Verify_was_set()
        {
            ITests test = Mock.Create<ITests>();

            test.Any = "Any";

            Mock.VerifySet(() => test.Any, "Any");
        }

        [Test]
        public void Verify_was_set_multiple_times()
        {
            ITests test = Mock.Create<ITests>();

            test.Any = "Any";
            test.Any = "AnyOne";
            test.Any = "AnyTwo";

            Mock.VerifySet(() => test.Any, "Any");
            Mock.VerifySet(() => test.Any, "AnyOne");
            Mock.VerifySet(() => test.Any, "AnyTwo");
        }

        [Test]
        [ExpectedException(typeof(VerifyException))]
        public void Verify_not_called_on_recursive_mocks_throws_verify_exception()
        {
            IBinaryTree<int> tree = Mock.Create<IBinaryTree<int>>();

            Mock.VerifyNotCalled(() => tree.Left.Right.GetAllDescendants());
        }
    }
}
